package org.hgg.workflow.core.engine;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * @author Jarvis
 * @date 2023/12/28 20:57
 */
public class CompletableFutureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        Dagraph dagraph = new Dagraph();
        // F\
        //A->B->D
        //C   ->D
        //A ->  E
        NodeTask a = new NodeTask("A", 2);
        NodeTask b = new NodeTask("B", 10);
        NodeTask c = new NodeTask("C", 1);
        NodeTask d = new NodeTask("D", 2);
        NodeTask e = new NodeTask("E", 2);
        NodeTask f = new NodeTask("F", 3);

        CompletableFutureWapper.add(a, b, c, d, e, f);

        dagraph.addDependency(a, b);
        dagraph.addDependency(b, d);
        dagraph.addDependency(c, d);
        dagraph.addDependency(a, e);
        dagraph.addDependency(f, b);

        // Map<String, CompletableFuture<Void>> futureMap = new HashMap<>();


     /*   CompletableFuture<Void> fa = CompletableFuture.runAsync(a);
        CompletableFuture<Void> fc = CompletableFuture.runAsync(c);
        CompletableFuture<Void> fb = CompletableFuture.completedFuture(fa).thenRunAsync(b);

        CompletableFuture<Void> fd = CompletableFuture.allOf(fb, fc).thenRunAsync(d);

        CompletableFuture<Void> fe = CompletableFuture.completedFuture(fa).thenRunAsync(e);
        CompletableFuture<Void> ff = CompletableFuture.completedFuture(fb).thenRunAsync(f);

        fe.join();
        fd.join();*/

        Map<String, CompletableFuture<Void>> futureMap = new HashMap<>();
        for (NodeTask node : dagraph.getRootNodes()) {
            doNodes(node.getChildren());
        }

        dagraph.nodeMap.values().stream().filter(node->node.isLeaf()).forEach(node->{

            //处理叶子几点
            if (node.getParents().isEmpty()){
                   node.getFuture().thenRunAsync(node).join();
            }else if(node.getParents().size() == 1){
                CompletableFuture<Void> future = node.getParents().get(0).getFuture();
                CompletableFuture.completedFuture(future).thenRunAsync(node);
            }else if(node.getParents().size()>1){

            }

        });


    }

    static void doNodes(List<NodeTask> tasks){
        for (NodeTask task : tasks) {
            doNode(task);
        }
    }

    static void doNode(NodeTask node){
        if (node.getChildren().isEmpty()) {
            CompletableFutureWapper wapper = CompletableFutureWapper.futureWapperMap.get(node.getName());
            wapper.getCf().join();
        }else {
            doNodes(node.getChildren());
        }
    }

    static void processNode(List<NodeTask> tasks, Map<String, CompletableFuture<Void>> futureMap) {

        for (NodeTask nodeTask : tasks) {
            if (nodeTask.getParents().isEmpty()) {
                futureMap.putIfAbsent(nodeTask.getName(), CompletableFuture.runAsync(nodeTask));
            } else if (nodeTask.getParents().size() == 1) {
                NodeTask parent = nodeTask.getParents().get(0);
                CompletableFuture<Void> cf = CompletableFuture.completedFuture(parent.getName()).thenRunAsync(nodeTask);
                futureMap.put(nodeTask.getName(), cf);
            } else {
                //如果有多个父节点，futureMap 可能为空
            }
        }

    }

    void processNode(NodeTask nodeTask, Map<String, CompletableFuture<Void>> futureMap) {
        List<NodeTask> parents = nodeTask.getParents();
        if (parents.isEmpty()) {
            futureMap.putIfAbsent(nodeTask.getName(), CompletableFuture.runAsync(nodeTask));
        } else if (parents.size() == 1) {
            NodeTask parent = parents.get(0);
            CompletableFuture<Void> future = futureMap.get(parent.getName());
            future.thenRunAsync(nodeTask);
        } else {
            CompletableFuture<Void>[] arr = new CompletableFuture[parents.size()];
            for (int i = 0; i < parents.size(); i++) {
                String name = parents.get(i).getName();
                arr[i] = futureMap.get(name);
            }
            futureMap.put(nodeTask.getName(), CompletableFuture.allOf(arr).thenRunAsync(nodeTask));
        }
    }

    public static class Dagraph {
        private Set<NodeTask> rootNodes;

        private Map<String, NodeTask> nodeMap = new HashMap<>();

        private Map<String, CompletableFuture<Void>> futureMap = new HashMap<>();

        public void addDependency(NodeTask source, NodeTask target) {
            NodeTask sourceNode = restoreMap(source);
            NodeTask targetNode = restoreMap(target);
            addEdges(sourceNode, targetNode);
        }

        NodeTask restoreMap(NodeTask nodeTask) {
            nodeMap.putIfAbsent(nodeTask.getName(), nodeTask);
            return nodeTask;
        }

        private void addEdges(final NodeTask source, final NodeTask target) {
            if (!source.equals(target)) {
                source.getChildren().add(target);
                target.getParents().add(source);
            }
        }

        public Set<NodeTask> getRootNodes() {
            if (rootNodes == null) {
                rootNodes = new HashSet<>();
                for (NodeTask node : nodeMap.values()) {
                    if (node.getParents().isEmpty()) {
                        rootNodes.add(node);
                    }
                }
            }
            return rootNodes;
        }

        public NodeTask getNode(String name) {
            return nodeMap.get(name);
        }
    }

    public static class CompletableFutureWapper {

        public static Map<String, CompletableFutureWapper> futureWapperMap = new HashMap<>();
        private NodeTask task;

        public CompletableFutureWapper(NodeTask task) {
            this.task = task;
        }

        public static void add(NodeTask... nodeTasks) {
            for (NodeTask nodeTask : nodeTasks) {
                futureWapperMap.put(nodeTask.getName(), new CompletableFutureWapper(nodeTask));
            }
        }

        public CompletableFuture<Void> getCf() {

            if (task.getParents().isEmpty()) {
                return CompletableFuture.runAsync(task);
            }

            if (task.getParents().size() == 1) {
                CompletableFutureWapper wapper = futureWapperMap.get(task.getParents().get(0).getName());
                return CompletableFuture.completedFuture(wapper.getCf()).thenRunAsync(task);
            }

            CompletableFuture<Void>[] arr = new CompletableFuture[task.getParents().size()];
            for (int i = 0; i < task.getParents().size(); i++) {
                NodeTask p = task.getParents().get(i);
                CompletableFutureWapper wapper = futureWapperMap.get(p.getName());
                arr[i] = wapper.getCf();
            }

            return CompletableFuture.allOf(arr).thenRunAsync(task);
        }

    }

}
